package com.gitee.loyo.controller;

import com.gitee.loyo.*;
import com.gitee.loyo.page.LogPage;
import com.gitee.loyo.utils.HttpUtils;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.RequestOptions;
import io.vertx.core.json.JsonObject;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;

import javax.ws.rs.*;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.List;

public class LogController {

    @Path("/log/*")
    @GET
    public void openLogPage(HttpServerRequest request, Promise<Void> promise){
        String domain = new String(Base64.getUrlDecoder().decode(request.path().substring("/log/".length())), StandardCharsets.UTF_8);
        if(!Bridges.contains(domain)){
            request.response().setStatusCode(404).setStatusMessage("Not Found Bridge").end();
            promise.complete();
            return;
        }

        List<Logs.RequestLog> logs = Logs.logs.getOrDefault(domain, Collections.emptyList());
        Collections.sort(logs, (o1, o2) -> o2.startTime.compareTo(o1.startTime));
        LogPage logPage = new LogPage(Bridges.get(domain), Collections.unmodifiableList(logs));
        Template tmpl = Global.VELOCITY_ENGINE.getTemplate("vm/log.vm");
        VelocityContext ctx = new VelocityContext();
        ctx.put("page", logPage);
        StringWriter output = new StringWriter();
        tmpl.merge(ctx, output);
        Pages.renderPage(request.response(), output.toString());
        promise.complete();
    }

    @Path("/log/*")
    @DELETE
    public void clearLogs(HttpServerRequest request, Promise<Void> promise){
        String domain = new String(Base64.getUrlDecoder().decode(request.path().substring("/log/".length())), StandardCharsets.UTF_8);
        Logs.logs.remove(domain);
        request.response().setStatusCode(200).end();
        promise.complete();
    }

    @Path("/log/*")
    @POST
    public void retry(HttpServerRequest request, Promise<Void> promise){
        String domain = new String(Base64.getUrlDecoder().decode(request.path().substring("/log/".length())), StandardCharsets.UTF_8);

        Future.<String>future(p ->
            HttpUtils.getBodyAsString(request, p)
        ).map(JsonObject::new).compose(obj -> {
            Long num = obj.getLong("num");
            if(num == null){
                return Future.failedFuture("not found num");
            }
            Logs.RequestLog log = Logs.logs.getOrDefault(domain, Collections.emptyList())
                .stream()
                .filter(requestLog ->
                    requestLog.num == num
                ).findFirst().orElse(null);
            if(log == null){
                return Future.failedFuture("not found log");
            }
            return Future.succeededFuture(log);
        }).compose(requestLog -> {
            if(requestLog.in.method == null || requestLog.in.uri == null || requestLog.in.headers == null){
                return Future.failedFuture("invalid request log");
            }
            RequestOptions requestOptions = new RequestOptions().setMethod(requestLog.in.method)
                .setURI(requestLog.in.uri)
                .setHeaders(requestLog.in.headers)
                .setTimeout(Global.LOG_RETRY_TIMEOUT);
            return Application.retryHttpClient.request(requestOptions).compose(httpClientRequest -> {
                if(requestLog.in.body == null || requestLog.in.body.length() == 0){
                    return httpClientRequest.send().compose(httpClientResponse ->
                        Future.future(p ->
                            httpClientResponse.endHandler(avoid -> p.complete())
                        )
                    );
                }else{
                    return httpClientRequest.send(requestLog.in.body).compose(httpClientResponse ->
                        Future.future(p ->
                            httpClientResponse.endHandler(avoid -> p.complete())
                        )
                    );
                }
            });
        }).onComplete(ar -> {
            if(ar.failed()){
                ar.cause().printStackTrace();
                request.response().setStatusCode(400).end(ar.cause().getMessage());
            }else{
                request.response().setStatusCode(200).end();
            }
            promise.complete();
        });
    }

    @Path("/log/*")
    @HEAD
    public void checkNewLogs(HttpServerRequest request, Promise<Void> promise){
        String domain = new String(Base64.getUrlDecoder().decode(request.path().substring("/log/".length())), StandardCharsets.UTF_8);
        List<Logs.RequestLog> logs = Logs.logs.getOrDefault(domain, Collections.emptyList());
        Collections.sort(logs, (o1, o2) -> o2.startTime.compareTo(o1.startTime));
        long num = Long.valueOf(request.getHeader("X-Log-Latest"));
        if(!logs.isEmpty() && logs.get(0).num > num){
            request.response().setStatusCode(201).end();
        }else{
            request.response().setStatusCode(204).end();
        }
        promise.complete();
    }
}
